import {Injectable} from '@angular/core';
import {TpStorageService} from './tp-storage.service';
import {User} from '../entity/user';
import {BehaviorSubject, Observable} from 'rxjs';
import {TpHttpService} from './tp-http.service';
import {BaseResponse} from '../entity/base-response';
import {UserData} from '../entity/user-data';
import {MessageService} from './message.service';
import {AlertMessage} from '../entity/alert-message';
import {DocFocusService} from './doc-focus.service';
import {MenuService} from './menu.service';
import {ArrayUtils} from '../utils/array-utils';
import {tap} from 'rxjs/operators';

@Injectable({
    providedIn: 'root'
})
export class UserService {
    private userBehavior = new BehaviorSubject({});

    userObservable = this.userBehavior.asObservable();

    private user: User;

    constructor(private tpStorageService: TpStorageService,
                private httpService: TpHttpService,
                private messageService: MessageService,
                private docFocusService: DocFocusService,
                private menuService: MenuService) {
        const userService = this;

        userService.checkUserState();
        setInterval(() => {
                userService.checkUserState();
            }, 1000
        );
    }

    setUser(user: User): void {
        this.user = user;
        this.tpStorageService.set(TpStorageService.KEY_AUTH, JSON.stringify(user));
        this.userBehavior.next(user);
    }

    getUser(): User {
        return this.user;
    }

    getToken(): string {
        if (this.user) {
            return this.user.token;
        }
        return '';
    }

    setCurrentUrl(url: string) {
        this.tpStorageService.set(TpStorageService.KEY_LAST_URL, url);
    }

    getUserByToken(): Observable<BaseResponse<User>> {
        return this.httpService.get('/api/user/my');
    }

    loadUserByToken() {
        this.getUserByToken()
            .subscribe((resp: BaseResponse<User>) => {
                this.userLogin(resp);
            });
    }

    public userLogin(resp: BaseResponse<User>) {
        if (resp.code === 200 && resp.data) {
            this.tpStorageService.set(TpStorageService.KEY_TP_DOC_TOKEN, resp.data.token);
            this.loadUserData(resp.data);
            this.messageService.add(<AlertMessage>{
                id: 'user-login',
                type: 'success',
                html: `欢迎${resp.data.name}归来，正在加载用户数据...`,
                time: 5
            });
        }
    }

    logout() {
        // this.httpService.del(`/api/oauth2/${this.user.loginCode}/logout`).subscribe((resp: BaseResponse) => {
        // });
        this.tpStorageService.delete(TpStorageService.KEY_TP_DOC_TOKEN);
        this.tpStorageService.delete(TpStorageService.KEY_AUTH);
        this.user = null;
        this.userBehavior.next(null);
    }

    /**
     * 是否是 docId的所有者
     * @param {string} docId 文档ID
     * @returns {boolean}
     */
    isDocOwner(docId: string): boolean {
        if (this.user && this.user.docsOwner) {
            return ArrayUtils.indexOf(this.user.docsOwner, docId) > -1;
        }
        return false;
    }

    asOwner(docId: string, userId?: string): Observable<boolean> {
        let observable: Observable<boolean>;
        if (!userId) {
            // 如果没有指定用户ID时，使用当前用户ID
            observable = this.httpService.post(`/api/doc/${docId}/owner/`, null);
        } else {
            observable = this.httpService.post(`/api/doc/${docId}/owner/${userId}/`, null);
        }

        return observable.pipe(
            tap(result => {
                // 更新用户状态
                const docs = this.user.docsOwner;
                if (docs) {
                    if (!ArrayUtils.contain(docs, docId)) {
                        docs.push(docId);
                        // 保存
                        this.setUser(this.user);
                    }
                }
            })
        );
    }

    /**
     * 移除文档的所有者
     * @param {string} docId
     * @param {string} userId
     */
    removeOwner(docId: string, userId?: string): Observable<boolean> {
        let observable: Observable<boolean>;
        if (!userId) {
            // 如果没有指定用户ID时，使用当前用户ID
            observable = this.httpService.del(`/api/doc/${docId}/owner/`);
        } else {
            observable = this.httpService.del(`/api/doc/${docId}/owner/${userId}/`);
        }

        return observable.pipe(
            tap(result => {
                // 更新用户状态
                const docs = this.user.docsOwner;
                if (docs) {
                    if (ArrayUtils.remove(docs, docId)) {
                        // 保存
                        this.setUser(this.user);
                    }
                }
            })
        );
    }

    /**
     * 加载用户数据
     */
    private loadUserData(user: User) {
        this.httpService
            .get('/api/user/data')
            .subscribe((userData: UserData) => {
                this.setUser(user);
                if (!userData) {
                    return;
                }
                // 合并
                if (userData.expandingMenus) {
                    // 合并菜单状态
                    this.menuService.mergeExpandingMenus(userData.expandingMenus);
                }
                if (userData.focusDocIds) {
                    // 合并关注文档
                    this.docFocusService.mergeFocusDocs(userData.focusDocIds);
                }
                // 更新当前界面
                this.messageService.removeById('user-login');
                this.messageService.add(<AlertMessage>{
                    id: 'user-data-load',
                    type: 'success',
                    html: `欢迎${this.getUser().name}归来，用户数据已经同步...`,
                    time: 5
                });
            });
    }

    private checkUserState(): void {
        const user = this.tpStorageService.getObj(TpStorageService.KEY_AUTH);

        // 检查是否过期
        const now = (new Date()).valueOf();
        if (user && user.tokenExpired < now) {
            // 已经过期
            this.tpStorageService.clearUser();
            this.userBehavior.next(null);
            return;
        }

        if (!this.equals(this.user, user)) {
            this.user = user;
            this.userBehavior.next(user);
        }
    }

    private equals(user1: User, user2: User): boolean {
        if (!user1) {
            if (user2) {
                return false;
            } else {
                return true;
            }
        }
        if (!user2) {
            return false;
        }
        return user1.token === user2.token && user1.name === user2.name && user1.id === user2.id;
    }
}
